using System;
using System.IO;
using System.Windows.Forms;
using System.IO.Compression;
using System.Collections.Generic;
using System.Threading.Tasks;
using System.Threading;
using System.Runtime.InteropServices;
using Microsoft.Win32.SafeHandles;

namespace FileTransfer
{
    public partial class frmMain : Form
    {
        public frmMain()
        {
            InitializeComponent();
        }
        IntPtr handle = IntPtr.Zero;
        FileStream fileStream = null;
        private void btLoadAndSend_Click(object sender, EventArgs e)
        {
            if (openFileDialog.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    var fileOpenPath = openFileDialog.FileName;
                    btLoadAndSend.Enabled = false;
                    Task.Factory.StartNew(() =>
                    {
                        byte[] filename = System.Text.Encoding.UTF8.GetBytes(Path.GetFileName(fileOpenPath));
                        using (FileStream fs = File.OpenRead(fileOpenPath))
                        {
                            fileStream.Write(BitConverter.GetBytes(fs.Length), 0, 8);
                            fileStream.Write(BitConverter.GetBytes(filename.Length), 0, 4);
                            fileStream.Write(filename, 0, filename.Length);
                            fs.CopyTo(fileStream);
                            fileStream.Flush();
                        }
                    }).ContinueWith(task => { btLoadAndSend.Enabled = true; }, CancellationToken.None, TaskContinuationOptions.None, TaskScheduler.Current);
                }
                catch (Exception ex)
                {
                    btLoadAndSend.Enabled = true;
                    MessageBox.Show("Somethings gone wrong:\n" + ex.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error);
                }
            }
        }

        static void StartFile(string filename)
        {
            Task.Factory.StartNew(() =>
            {
                try
                {
                    System.Diagnostics.Process.Start(filename);
                }
                catch { }
            });
        }

        private void frmMain_Load(object sender, EventArgs e)
        {
            handle = WtsApi32.WTSVirtualChannelOpen(IntPtr.Zero, -1, "FTFTA1");
            Task.Factory.StartNew(() =>
            {
                IntPtr tempPointer = IntPtr.Zero;
                uint pointerLen = 0;
                if (WtsApi32.WTSVirtualChannelQuery(handle, WtsApi32.VirtualClass.FileHandle, out tempPointer, ref pointerLen))
                {
                    IntPtr realPointer = Marshal.ReadIntPtr(tempPointer);
                    WtsApi32.WTSFreeMemory(tempPointer);
                    var fileHandle = new SafeFileHandle(realPointer, false);
                    fileStream = new FileStream(fileHandle, FileAccess.ReadWrite, 0x640, true);
                    var os = new ObjectState();
                    os.RDPStream = fileStream;
                    var iar = fileStream.BeginRead(os.Buffer, 0, 8, CallBack, os);
                }
                else
                    Close();
            });
        }


        private void frmMain_FormClosed(object sender, FormClosedEventArgs e)
        {
            fileStream.Dispose();
            bool ret = WtsApi32.WTSVirtualChannelClose(handle);
        }


        enum FileDownloadStatus
        {
            Length,
            NameLength,
            Name,
            Body,
        }

        class ObjectState
        {
            public int NameLength = 0;
            public string Name = string.Empty;
            public long Length = 0;
            public FileDownloadStatus State = FileDownloadStatus.Length;
            public byte[] Buffer = new byte[0x640];
            public FileStream File = null;
            public FileStream RDPStream = null;
        }


        static void CallBack(IAsyncResult iar)
        {
            if (iar.IsCompleted || iar.CompletedSynchronously)
            {
                var os = iar.AsyncState as ObjectState;
                int readed = 0;
                try
                {
                    readed = os.RDPStream.EndRead(iar);
                }
                catch (IOException ex) { MessageBox.Show(ex.Message); }
                if (readed != 0)
                {
                    switch (os.State)
                    {
                        case FileDownloadStatus.Length:
                            os.Length = BitConverter.ToInt64(os.Buffer, 0);
                            os.State = FileDownloadStatus.NameLength;
                            os.RDPStream.BeginRead(os.Buffer, 0, 4, CallBack, os);
                            break;
                        case FileDownloadStatus.NameLength:
                            os.NameLength = BitConverter.ToInt32(os.Buffer, 0);
                            os.State = FileDownloadStatus.Name;
                            os.RDPStream.BeginRead(os.Buffer, 0, os.NameLength, CallBack, os);
                            break;
                        case FileDownloadStatus.Name:
                            os.Name = Path.Combine(Path.GetTempPath(), System.Text.Encoding.UTF8.GetString(os.Buffer, 0, readed));
                            os.File = File.OpenWrite(os.Name);
                            os.State = FileDownloadStatus.Body;
                            os.RDPStream.BeginRead(os.Buffer, 0, Math.Min(os.Buffer.Length, (int)os.Length), CallBack, os);
                            break;
                        case FileDownloadStatus.Body:
                            os.Length -= readed;
                            os.File.Write(os.Buffer, 0, readed);
                            if (os.Length == 0)
                            {
                                os.File.Dispose();
                                StartFile(os.Name);
                                var newos = new ObjectState();
                                newos.RDPStream = os.RDPStream;
                                os.RDPStream.BeginRead(newos.Buffer, 0, 8, CallBack, newos);
                            }
                            else
                            {
                                if (!os.RDPStream.SafeFileHandle.IsInvalid)
                                    os.RDPStream.BeginRead(os.Buffer, 0, Math.Min(os.Buffer.Length, (int)os.Length), CallBack, os);
                            }
                            break;
                    }
                }
            }
        }
    }
}